package sim;

import force.Force;
import force.GlobalForceApplier;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import object.mass.Mass;



/**
 * Simulates objects moving around in a bounded environment.
 * 
 * @author Robert C. Duvall, edited by Niel Lebeck and Wynne Lok
 */
public class Simulation {
    private List<Assembly> myAssemblies;
    private Canvas myContainer;
    private GlobalForceApplier myGlobalForceApplier;

    /**
     * Create a Canvas with the given size.
     * @param container the canvas used
     */
    public Simulation (Canvas container) {
        myAssemblies = new ArrayList<Assembly>();
        myContainer = container;
        myGlobalForceApplier = new GlobalForceApplier(this);
    }

    /**
     * Paint all shapes on the canvas.
     * 
     * @param pen used to paint shape on the screen
     */
    public void paint (Graphics2D pen) {
        for (Assembly a : myAssemblies) {
            a.paint(pen);
        }
    }

    /**
     * Called by each step of timer, multiple times per second.
     * 
     * This should update the state of the animated shapes by just
     * a little so they appear to move over time.
     * @param dt the time
     */
    public void update (double dt) {
        for (Assembly a : myAssemblies) {
            a.updateMovers(dt);
        }
    }
/**
 * Adds the mouse click spring and mass
 * @param a what to add
 */
    public void addAssembly(Assembly a) {
        myAssemblies.add(a);
    }
/**
 * Removes the mouse click
 * @param a what to remove
 */
    public void removeAssembly(Assembly a) {
        myAssemblies.remove(a);
    }

    /**
     * Returns size (in pixels) of the game area.
     */
    public Dimension getSize () {
        return myContainer.getSize();
    }
/**
 * Applys the global forces.
 * @param m the mass the force is applied to
 */
    public void applyGlobalForces(Mass m) {
        myGlobalForceApplier.applyForces(m);
    }
/**
 * Gets the center of the mass of the assembly.
 * @return center of mass
 */
    public Point2D getCenterOfMass() {
        double xMassSum = 0, yMassSum = 0, totalMass = 0;
        for (Assembly a : myAssemblies) {
            for (Mass m : a.getMassList()) {
                xMassSum += m.getCenter().getX() * m.getMass();
                yMassSum += m.getCenter().getY() * m.getMass();
                totalMass += m.getMass();
            }
        }
        double centerOfMassX = xMassSum / totalMass;
        double centerOfMassY = yMassSum / totalMass;
        return new Point((int)centerOfMassX, (int)centerOfMassY);
    }
/**
 * Gets closest mass for mouse click
 * @param targetMass the mass we want
 * @return the closest mass
 */
    public Mass getClosestMass(Mass targetMass) {
        double closestDistance = Double.POSITIVE_INFINITY;
        Mass closestMass = null;
        for (Assembly a : myAssemblies) {
            for (Mass m : a.getMassList()) {
                double distance = Force.distanceBetween(targetMass.getCenter(), m.getCenter());
                if (distance < closestDistance) {
                    closestDistance = distance;
                    closestMass = m;
                }
            }
        }
        return closestMass;
    }
/**
 * Get globalforceapplier.
 * @return the globalforceapplier
 */
    public GlobalForceApplier getGlobalForceApplier() {
        return myGlobalForceApplier;
    }
/**
 * Tells the classes to turn on or off.
 * @param keycode input from user
 */
    public void toggleForce(int keycode) {
        myGlobalForceApplier.toggleForce(keycode);
    }
}
